#define _CRT_SECURE_NO_WARNINGS 1

// 二级指针
//#include <stdio.h>
//
//int main()
//{
//	int a = 10;
//	int* p = &a; // 取出a的地址
//
//	//p是指针变量，是一级指针
//	int** pp = &p;
//	// int*** ppp = &pp; (用的最少)
//
//	return 0;
//}

// 用指针数组来模拟二维数组
// 模拟出二维数组的效果，但不是二维数组
// 二维数组的每一行是一个一维数组

//#include <stdio.h>
//
//int main()
//{
//	int arr1[] = { 1,2,3,4,5 };
//	int arr2[] = { 2,3,4,5,6 };
//	int arr3[] = { 3,4,5,6,7 };
//
//	int* arr[] = { arr1, arr2, arr3 };
//	for (int i = 0; i < 3; i++) {
//			for (int j = 0; j < 5; j++) {
//				printf("%d ", arr[i][j]);
//			}
//			printf("\n");
//	}
//
//	return 0;
//}

//#include <stdio.h>
//
//int main()
//{
//	// char ch = 'w';
//	// char* pc = &ch; // pc就是字符指针
//
//	// 不是把字符串abcdef\0存放在p中，而是把第一个字符的地址存放在p中
//	const char* p = "abcdef"; 
//	printf("%c\n", *p);
//
//	// 1.可以把字符串想象成一个字符数组，但是这个数组是不能修改的
//	// 2.当常量字符串出现在表达式的时候，他的值是第一个字符的地址
//	printf("%c\n", "abcdef"[3]);
//	printf("%c\n", p[3]);
//
//	//p[3] = 'q'; // err
//
//	return 0;
//}

//#include <stdio.h>
//
//int main()
//{
//	char str1[] = "hello bit";
//	char str2[] = "hello bit";
//
//	// 常量字符串内容相同的话，只会保留一份
//	const char* str3 = "hello bit";
//	const char* str4 = "hello bit";
//
//	if (str1 == str2)
//		printf("str1 and str2 are same\n");
//	else
//		printf("str1 and str2 are not same\n");
//
//	if (str3 == str4)
//		printf("str1 and str2 are same\n");
//	else
//		printf("str1 and str2 are not same\n");
//
//	return 0;
//}

//#include <stdio.h>
//
//int main()
//{
//	// 指针数组：是数组，是存放指针的数组
//	// 数组指针：是指针，是指向数组的指针
//
//	int n = 100;
//	int* pn = &n;
//
//	char ch = 'w';
//	char* pc = &ch;
//
//	float f = 3.14f;
//	float* pf = &f;
//
//	int* p1[10]; //p1是数组，数组10个元素，每个元素的类型是int*，所以p1是指针数组
//	int(*p2)[10]; //p2是指针，指针指向的是数组，数组有10个元素，每个元素的类型是int
//
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	// 为什么要加圆括号？
//	// 因为[]的优先级高于*运算符的优先级
//
//	/*
//	* int (*parr)[]
//	* *parr -> parr是一个指针
//	* (*parr)[] -> 我是什么类型的指针？ 我是数组类型的指针
//	* int (*parr)[]这个数组的元素是int
//	*/
//	int (*parr)[10] = &arr;
//	// parr 就是数组指针
//
//	printf("%d \n", (*parr)[0]);
//
//	return 0;
//}

//#include <stdio.h>
//
//int main()
//{
//	int a = 10, b = 20, c = 30;
//	int* arr[] = { &a, &b, &c };
//	int* (*parr)[] = &arr;
//
//	/*
//	* *parr parr是一个指针
//	* (*parr)[]是一个数组类型的指针
//	* 这个数组的每个元素是什么类型呢
//	* int* (*parr)[]数组的每一个元素是int*
//	*/
//
//	return 0;
//}

//#include <stdio.h>
//
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	int* p1 = arr;
//	int* p2 = &arr[0];
//	int(*p3)[10] = &arr;
//
//	return 0;
//}

// 二维数组传参的本质
// 二维数组传参，形参写的是二维数组
// 一维数组传参，形参可以是数组，也可以是指针
// 1.写成数组更加直观，为了方便理解
// 2.写成指针是因为数组传参，传递的是数组第一个元素的地址
// 对于二维数组，首元素其实就是第一行的地址

#include <stdio.h>

//void Print(int arr[3][5], int r, int c)
//{
//	for (int i = 0; i < 3; i++) {
//		for (int j = 0; j < 5; j++) {
//			printf("%d ", arr[i][j]);
//		}
//		putchar('\n');
//	}
//}

//void Print(int(*parr)[5], int r, int c)
//{
//	for (int i = 0; i < 3; i++) {
//		for (int j = 0; j < 5; j++) {
//			printf("%d ", *(*(parr + i) + j));
//		}
//		putchar('\n');
//	}
//}
//
//int main()
//{
//	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
//	Print(arr, 3, 5); // 打印arr数组的内容,传递的是第一行的地址
//
//	return 0;
//}
// 二维数组的数组名表示的就是第一行的地址，是一维数组的地址

// 函数指针变量
// 函数指针变量应该是用来存放函数地址的，未来通过地址可以调用函数的
//#include <stdio.h>
//
//int Add(int x, int y)
//{
//	return x + y;
//}
//
//int main()
//{
//	int a = 10;
//	int* pa = &a; // 整形指针
//
//	int arr[10] = { 0 };
//	int(*parr)[10] = &arr; // 数组指针
//
//	// 函数指针变量
//	printf("%p\n", &Add);
//	printf("%p\n", Add);
//
//	// 数组名：数组首元素的地址
//	// &数组名：整个数组的地址
//	// 函数名和&函数名都是函数的地址
//	int (*pf)(int, int) = &Add; // pf就是函数指针变量
//
//	int ret = Add(3, 5);
//	printf("%d\n", ret); // 8
//
//	int ret2 = (*pf)(4, 9);
//	printf("%d\n", ret2); // 13
//
//	int (*pf2)(int, int) = Add;
//	int ret3 = (*pf2)(5, 6);
//	printf("%d\n", ret3); // 11
//
//	int ret4 = pf2(5, 6);
//	printf("%d\n", ret4);
//
//	return 0;
//}

//#include <stdio.h>
//
//char* test(int a, char c)
//{
//	// ...
//	return NULL;
//}
//
//int main()
//{
//	// 类型解析：
//	// char* 指的是指向函数的返回类型
//	// pt是函数指针变量名
//	// int x, char y(x和y写上或者省略都是可以的)是指向函数的参数类型和个数的交代
//
//	char* (*pt)(int , char) = test;
//
//	return 0;
//}

// 两段有趣的代码
//#include <stdio.h>
//
//int main()
//{
//	// void(*)()函数指针类型
//	// 这段代码其实是一次函数调用
//	// 先把0强制转换成一个函数的地址，这个函数没有参数，且返回类型是void
//	// 去调用0地址处的函数
//	// ((*void(*)())0)();
//
//	return 0;
//}

//#include <stdio.h>
//
//void(* signal(int, void(*)(int)))(int);
//// 上述代码是函数声明
//// signal是一个函数，signal函数的参数有2个
//// 第一个是int类型，第二个是函数指针类型，该指针指向的函数参数是int，返回类型是void
//// signal函数的返回类型是这种类型的void(*)(int)函数指针
//
//// 对于函数指针类型的重命名，要注意位置在*号后面
//typedef unsigned int uint;
//typedef int(*pArr_t)[10];
//
//int main()
//{
//	unsigned int num1;
//	uint num2;
//
//	pArr_t pa;
//	int(*pb)[10];
//
//	return 0;
//}



